import { Scene } from '@babylonjs/core/scene';
import { AbstractPick, IAbstractPickOptions } from '../abstract-pick';
import { AbstractSelectConfig } from './config/abstract-select-config';
import { AbstractMesh } from '@babylonjs/core/Meshes/abstractMesh';
import { PickingInfo } from '@babylonjs/core/Collisions/pickingInfo';
import { CreateHandleTo, IHandle, RemoveHandle } from '../../data/data-handle';


/**
 * 点选对象
 * @param target 目标对象
 * @param config 选择配置
 */
interface ISelectPickTarget {
    target: any,
    config: AbstractSelectConfig<any>,
}

/**
 * 选择信息对象
 * @param pickingInfo 拾取信息
 * @param target 目标对象
 * @param config 选择配置
 */
interface ISelectInfo {
    pickingInfo: PickingInfo | null,
    target: any,
    config: AbstractSelectConfig<any>,
}

/**
 * 点选参数对象
 * @param useClick 是否使用单击
 * @param useDoubleClick 是否使用双击
 */
interface ISelectPickOptions extends IAbstractPickOptions {
    useClick?: boolean,
    useDoubleClick?: boolean,
}


/**
 * 点选
 * @param scene 所在场景
 * @param options 参数对象
 */
class SelectPick extends AbstractPick<ISelectPickTarget>{

    private _useClick: boolean;
    private _useDoubleClick: boolean;
    private _selectHandles: IHandle<ISelectInfo | null>[] = [];
    private _ta0: ISelectPickTarget[] = [];

    constructor(scene: Scene, options?: ISelectPickOptions) {
        super(scene, options);
        const ops = options || {};
        this._useClick = ops.useClick === undefined ? true : ops.useClick;
        this._useDoubleClick = ops.useDoubleClick || false;
    }

    /**
     * 添加选择时的回调函数对象
     * @param func 回调函数
     * @returns 回调函数对象
     */
    public onSelect(func: { (info: ISelectInfo | null): void }): IHandle<ISelectInfo | null> {
        return CreateHandleTo(func, this._selectHandles);
    }

    /**
     * 移除选择时的回调函数对象
     * @param handle 回调函数对象或其id
     */
    public removeSelectHandle(handle: number | IHandle<ISelectInfo | null>): void {
        RemoveHandle(handle, this._selectHandles);
    }

    protected compare(a: ISelectPickTarget, b: ISelectPickTarget): boolean {
        if (!a.config.test(b.target)) return false;
        return a.config.compare(a.target, b.target);
    }

    protected getMeshesFromTarget(target: ISelectPickTarget): AbstractMesh[] {
        return target.config.meshes(target.target);
    }

    protected clickHandle(pickInfo: PickingInfo | null): void {
        if (!this._useClick) return;
        this.selectHandle(pickInfo);
    }

    protected doubleClickHandle(pickInfo: PickingInfo | null): void {
        if (!this._useDoubleClick) return;
        this.selectHandle(pickInfo);
    }

    /**
     * 拾取选择函数
     * @param pickInfo 拾取信息
     */
    private selectHandle(pickInfo: PickingInfo | null): void {
        if (!pickInfo) {
            this._selectHandles.forEach(handle => handle.callback(null));
            return;
        }
        if (!pickInfo.pickedMesh) {
            this._selectHandles.forEach(handle => handle.callback(null));
            return;
        }
        const pm = pickInfo.pickedMesh;
        const ts = this._ta0;
        ts.length = 0;
        let tar: ISelectPickTarget | null = null;
        for (const t of this.targets) {
            const dph = t.config.isMeshOfTarget(pm, t.target);
            if (dph < 0) continue;
            ts[dph] = t;
        }
        for (const t of ts) {
            if (!t) continue;
            tar = t;
            break;
        }
        if (!tar) {
            this._selectHandles.forEach(handle => handle.callback(null));
            return;
        }
        this._selectHandles.forEach(handle => handle.callback(
            {
                pickingInfo: pickInfo,
                target: tar!.target,
                config: tar!.config,
            }
        ));
    }

}


export { SelectPick };
export type { ISelectPickTarget, ISelectPickOptions, ISelectInfo };